package semantica;

import java.util.ArrayList;
import semantica.tipos.Tipo;
import semantica.tipos.TipoBoolean;
import semantica.tipos.TipoCharacter;
import semantica.tipos.TipoFloat;
import semantica.tipos.TipoInteger;
import semantica.tipos.TipoNatural;

public class Semantica {

	/**
	 * 0- idDuplicado, 1-idNoDeclarado, 2-
	 * @param e1
	 * @param e2
	 * @return
	 */
	public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> e1, ArrayList<Boolean> e2){
		ArrayList<Boolean> errores = new ArrayList<Boolean>();
		for (int i = 0; i < e1.size(); i++)
			errores.add(e1.get(i) | e2.get(i));
		return errores;
	}
	
	/**
	 * Une 4 array list con errores haciendo una or logica 
	 * @param e1
	 * @param e2
	 * @param e3
	 * @param e4
	 * @return
	 */
	public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> e1, ArrayList<Boolean> e2,
			ArrayList<Boolean> e3, ArrayList<Boolean> e4){
		return unirErrores(unirErrores(e1,e2),unirErrores(e3,e4));
	}
        
        /**
         * Añade al array list de booleanos, la or de b con lo que habia dentro del array en la pos dada
         * @param err
         * @param b
         * @param pos
         * @return 
         */
        public static ArrayList<Boolean> unirErrores (ArrayList<Boolean> err, boolean b, int pos){
            ArrayList<Boolean> error = new ArrayList<Boolean>(err);
            error.set(pos, error.get(pos) | b);
            return error;
        }
	
	/**
	 * Devuelve un array list con la copia de otro que le entra por parametro
	 * @param e1
	 * @return
	 */
	public static ArrayList<Boolean> copiaErrores (ArrayList<Boolean> e1){
		return new ArrayList<Boolean>(e1);
	}
	
	/**
	 * Crea un atributo tipo error. Es una array list con todos los errores a false.
	 * @return
	 */
	public static ArrayList<Boolean> creaErrores (){
		ArrayList<Boolean> errores = new ArrayList<Boolean>();
		for (int i = 0; i < 2; i++) /**FIXME CAMBIAR POR EL NUMERO DE ERRORES QUE HAYA TOTAL*/
			errores.add(false);
		return errores; 
	}

	/**
	 * Indica si hay algun tipo de error en el atributo de entrada.
	 * @param e
	 * @return
	 */
	public static boolean hayError(ArrayList<Boolean> e){
		boolean hayErrores = false;
		for (int i = 0; i < e.size(); i++)
			hayErrores |= e.get(i);
		return hayErrores;
	}
	
	/**
	 * Une 2 tipos para ver si alguno es error tipo
	 * @param t1
	 * @param t2
	 * @return
	 */
	public static Tipo unirTipos (Tipo t1, Tipo t2){
		/**FIXME HACERLO BIEN*/
		return t1;
	}
	
	/**
	 * Une 4 tipos para ver si alguno es error tipo
	 * @param t1
	 * @param t2
	 * @param t3
	 * @param t4
	 * @return
	 */
	public static Tipo unirTipos (Tipo t1, Tipo t2, Tipo t3, Tipo t4){
		/**FIXME HACERLO BIEN*/
		return new Tipo();
	}
        
        /**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param cod2
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> dameCod(ArrayList<String> cod1, ArrayList<String> cod2, String str,
                        ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		cod.addAll(cod1);
                cod.addAll(cod2);
                cod.add(str);
		return cod;
	}
        
	
	/**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param cod2
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, ArrayList<String> cod2, String str,
                        ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                    cod.addAll(cod1);
                    cod.addAll(cod2);
                    cod.add(str);
                }
		return cod;
	}
        
        /**
	 * Concatena dos arraylist con un string
	 * @param cod1
	 * @param str
	 * @return
	 */	
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, String str, ArrayList<Boolean> err, Tipo tipo){
		ArrayList<String> cod = new ArrayList<String>();
		if (!hayError(err) && !tipo.getTipo().equals("errorTipo")){
                       cod.addAll(cod1);
                       cod.add(str);
                }
		return cod;
	}
	
	/**
	 * Concatena 4 array list de strings
	 * @param cod1
	 * @param cod2
	 * @param cod3
	 * @return
	 */
	public static ArrayList<String> concatenaCod(ArrayList<String> cod1, ArrayList<String> cod2, 
			ArrayList<String> cod3){
		ArrayList<String> cod = new ArrayList<String>();
		cod.addAll(cod1);
		cod.addAll(cod2);
		cod.addAll(cod3);
		return cod;
	}
	
	/**
	 * Crea un array list de string con el string que le entra como parametro
	 * @param str
	 * @return
	 */
	public static ArrayList<String> creaCod(String str){
		ArrayList<String> cod = new ArrayList<String>();
		cod.add(str);
		return cod;
	}
	
	/**
	 * Devuelve un array list vacios
	 * @return
	 */
	public static ArrayList<String> creaCod(){
		return new ArrayList<String>();
	}
        
        public static TablaDeSimbolos creaTS(AtributosDecConst decConst, int dir){
            TablaDeSimbolos ts = new TablaDeSimbolos();
            if (!decConst.getTipo().getTipo().equals("errorTipo"))
                ts.anadeTS(decConst.getId(), decConst.getClase(), decConst.getTipo(), dir);
            return ts;
        }
        
        /**FIXME NO HARA FALTA CUANDO SE PONGA GLOBAL LA TS */
        public static TablaDeSimbolos creaTS(TablaDeSimbolos ts, AtributosDecConst decConst, ArrayList<Boolean> err, Tipo tipo, int dir){
            TablaDeSimbolos tsNueva = new TablaDeSimbolos(ts);
            if (!hayError(err) && !tipo.getTipo().equals("errorTipo"))
                tsNueva.anadeTS(decConst.getId(), decConst.getClase(), decConst.getTipo(), dir);
            return tsNueva;
        }
        
        public static TablaDeSimbolos creaTS(TablaDeSimbolos ts, AtributosDecVar decVar, ArrayList<Boolean> err, Tipo tipo, int dir){
            TablaDeSimbolos tsNueva = new TablaDeSimbolos(ts);
            if (!hayError(err) && !tipo.getTipo().equals("errorTipo"))
                tsNueva.anadeTS(decVar.getId(), decVar.getClase(), decVar.getTipo(), dir);
            return tsNueva;
        }
        
        
        /**
         * FIXME hacer bien. No se si le entrara tambien la TS
         * @param tipo
         * @param clase 
         */
        public static int tamDe(Tipo tipo, String clase){
            return 1;
        }
        
        /**
         * FIXME hacer bieeeeeeeen
         * @param tipo
         * @param valor
         * @return 
         */
        public static float valorDe(Tipo tipo, String valor){
            if (tipo.getTipo().equals("boolean")){
                if (valor.equals("true")) return 1;
                else return 0;
            }
            if (tipo.getTipo().equals("character")){
                return 0; // valor del caracter /**FIXME valor del CHAR*/
            }
            if (tipo.getTipo().equals("natural"))
                return Integer.parseInt(valor);

            if (tipo.getTipo().equals("integer"))
                 return Integer.parseInt(valor);
            
            if (tipo.getTipo().equals("float"))
                 return Float.parseFloat(valor);
            
            return 0;
        }
      
      /**
         * FIXME hacer bieeeeeeeen
         * @param tipo
         * @param valor
         * @return 
         */
        public static Tipo tipoDe(Tipo tipo, String valor){
            return tipo;
        }
        
        /**
         * @param id
         * @param ts
         * @return 
         */
        public static Tipo tipoDe(String id, TablaDeSimbolos ts){
            if (ts.existeId(id))
                return ts.getTipo(id);
            else return new Tipo("errorTipo");
        }
          
        public static int numInstrucciones(ArrayList<String> cod){
            return cod.size();
        }
        
        /**
         * FIXME hacer bieeeeeeeen
         * @param tipo
         * @param valor
         * @return 
         */
        public static ArrayList<String> inicializarTipo(Tipo tipo){
            ArrayList<String> al = new ArrayList<String>(); 
            if (esTipoBasico(tipo))
                al.add("apila(0)");
            return al;
        }
        
        public static Tipo creaBoolean(){
            return new TipoBoolean();
        }
        
        public static Tipo creaCharacter(){
            return new TipoCharacter();
        }
        
        public static Tipo creaNatural(){
            return new TipoNatural();
        }
        
        public static Tipo creaInteger(){
            return new TipoInteger();
        }
        
        public static Tipo creaFloat(){
            return new TipoFloat();
        }

    private static boolean esTipoBasico(Tipo tipo) {
        return tipo.getTipo().equals("boolean") || tipo.getTipo().equals("character") ||
                tipo.getTipo().equals("natural") || tipo.getTipo().equals("integer") ||
                tipo.getTipo().equals("float");
    }
	
    
    public static int dameEtq (int etq, int suma, ArrayList<Boolean> err, Tipo tipo){
        if (!hayError(err) && !tipo.getTipo().equals("errorTipo"))
            return etq + suma;
        else 
            return etq;
    }
}
